#include <drivers/i2c.h>
#include <kernel/bus.h>
#include <kernel/initcall.h>
#include <kernel/syslog.h>
#include <iomode.h>


/**
 * @brief i2c bus probe function, it will probe i2c device
 * @param dev device struct
 * @return 0 success, -1 failed
 */
static int i2c_bus_probe(struct device *dev)
{
    struct i2c_device *i2cdev = to_i2c_device(dev);
    struct i2c_driver *i2cdrv = to_i2c_driver(dev->driver);

    if(i2cdrv && i2cdrv->probe) {
        return i2cdrv->probe(i2cdev);
    }

    return 0;
}


/**
 * @brief i2c bus remove function, it will remove i2c device
 * @param dev device struct
 * @return none
 */
static void i2c_bus_remove(struct device *dev)
{
    struct i2c_device *i2cdev = to_i2c_device(dev);
    struct i2c_driver *i2cdrv = to_i2c_driver(dev->driver);

    if(i2cdrv->remove) {
        i2cdrv->remove(i2cdev);
    }
}


/**
 * @brief i2c bus struct
 * @name: bus name
 * @match: match function
 * @probe: probe function
 * @remove: remove function
 */
struct bus_type i2c_bus = {
    .name = "i2c",
    .match = bus_simple_match,
    .probe = i2c_bus_probe,
    .remove = i2c_bus_remove,
};


/**
 * @brief i2c bus register function
 * @param none
 * @return 0 success, -1 failed
 */
static int i2c_bus_register(void)
{
    return bus_register(&i2c_bus);
}


bus_initcall(i2c_bus_register);


/**
 * @brief get i2c device gpio pin from device tree
 * @param node: device tree node
 * @param pin: i2c gpio pin
 * @return: 0: success, -1: failed
 */
int i2c_adapter_get_gpio_from_dt(fdt_node_t *node, struct i2c_gpio *pin)
{
    size_t scl, sda, afio = 0;
    if(fdt_read_prop_int(node, "afio", &afio)) {
        I2C_SYSLOG_ERROR("%s: not found afio", node->name);
        return -1;
    }

    if(fdt_read_prop_int(node, "scl-gpio", &scl)) {
        I2C_SYSLOG_ERROR("%s: not found scl pin", node->name);
        return -1;
    }
    else {
        if(gpio_request(scl)) {
            I2C_SYSLOG_ERROR("%s: scl gpio request failed", node->name);
            return -1;
        }

        I2C_DEBUG_TRACE("%s: scl gpio(%d) request success", node->name, scl);
        pin->scl = scl;
        gpio_direction_output(scl, IO_HIGH);
    }
    gpio_set_mux(scl, (IO_MODE_I2C_SCL << 8) | afio);

    if(fdt_read_prop_int(node, "sda-gpio", &sda)) {
        I2C_SYSLOG_ERROR("%s: not found sda pin", node->name);
        return -1;
    }
    if(gpio_request(sda)) {
        I2C_SYSLOG_ERROR("%s: sda gpio request failed", node->name);
        return -1;
    }
    I2C_DEBUG_TRACE("%s: sda gpio(%d) request success", node->name, sda);
    pin->sda = sda;
    gpio_set_mux(sda, (IO_MODE_I2C_SDA << 8) | afio);

    return 0;
}


/**
 * @brief get i2c device config from device tree
 * @param node: device tree node
 * @param i2c_dev: i2c device
 * @return: 0: success, -1: failed
 */
int i2c_device_get_config_from_dt(fdt_node_t *node, struct i2c_device *dev)
{
    const char *comp = fdt_read_prop_string(node, "compatible");
    if(comp == NULL) {
        I2C_SYSLOG_ERROR("no compatible property");
        return -1;
    }

    size_t addr;
    if(fdt_read_prop_int(node, "reg", &addr)) {
        I2C_SYSLOG_ERROR("%s: not found reg", node->name);
        return -1;
    }
    
    size_t speed = 10000;
    if(fdt_read_prop_int(node, "speed", &speed)) {
        I2C_SYSLOG_INFO("%s: not found speed, default: 10000", node->name);
    }
    else {
        I2C_SYSLOG_INFO("%s: speed %d", node->name, speed);
    }

    dev->device.name = node->name;
    dev->device.compatible = comp;
    dev->addr = addr;
    dev->speed = speed;
    dev->device.dt_node = node;

    return 0;
}
